/*
 * storage.c
 *
 *  Created on: April 15, 2020
 *      Author: hyjas@Hui
 */
/* Includes ------------------------------------------------------------------*/
#include "app_storage.h"
#include "main.h"
#include "spi.h"
#include "stdio.h"
/* Private typedef -----------------------------------------------------------*/
#define FLASH_CSN_ASSERT()	    (HAL_GPIO_WritePin(FLASH_CSN_GPIO_Port,FLASH_CSN_Pin,GPIO_PIN_RESET))
#define FLASH_CSN_DEASSERT()	(HAL_GPIO_WritePin(FLASH_CSN_GPIO_Port,FLASH_CSN_Pin,GPIO_PIN_SET))

#define FLASH_HANDLE    (&hspi1)
/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Exported variables --------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/
//static uint8_t spi_flash_read_byte(void);
static uint8_t spi_flash_send_byte(uint8_t byte);
//static uint16_t spi_flash_send_halfword(uint16_t half_word);
static void spi_flash_write_enable(void);
static void spi_flash_wait_for_write_end(void);

//static void spi_flash_start_read_sequence(uint32_t read_addr);

/* Private functions ---------------------------------------------------------*/

/*!
    \brief      read a byte from the SPI flash
    \param[in]  none
    \param[out] none
    \retval     byte read from the SPI flash
*/
uint8_t spi_flash_read_byte(void)
{
    return(spi_flash_send_byte(DUMMY_BYTE));
}
/*!
    \brief      start a read data byte (read) sequence from the flash
    \param[in]  read_addr: flash's internal address to read from
    \param[out] none
    \retval     none
*/
//void spi_flash_start_read_sequence(uint32_t read_addr)
//{
//    /* select the flash: chip select low */
//    SPI6_NSS_ASSERT();
//
//    /* send "read from memory " instruction */
//    spi_flash_send_byte(READ);
//
//    /* send the 24-bit address of the address to read from */
//    /* send read_addr high nibble address byte */
//    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
//    /* send read_addr medium nibble address byte */
//    spi_flash_send_byte((read_addr& 0xFF00) >> 8);
//    /* send read_addr low nibble address byte */
//    spi_flash_send_byte(read_addr & 0xFF);
//}

/*!
    \brief      send a byte through the SPI interface and return the byte received from the SPI bus
    \param[in]  byte: byte to send
    \param[out] none
    \retval     the value of the received byte
*/
uint8_t spi_flash_send_byte(uint8_t byte)
{
    uint8_t tempData;
	HAL_SPI_TransmitReceive(FLASH_HANDLE, &byte,&tempData,1,100);
    return tempData;
}
/*!
    \brief      enable the write access to the flash
    \param[in]  none
    \param[out] none
    \retval     none
*/
void spi_flash_write_enable(void)
{
    /* select the flash: chip select low */
    FLASH_CSN_ASSERT();
    /* send "write enable" instruction */
    spi_flash_send_byte(WREN);

    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();
}

/*!
    \brief      send a half word through the SPI interface and return the half word received from the SPI bus
    \param[in]  half_word: half word to send
    \param[out] none
    \retval     the value of the received byte
*/
//uint16_t spi_flash_send_halfword(uint16_t half_word)
//{
//    uint16_t tempData;
//	HAL_SPI_TransmitReceive(&hspi6, (uint8_t *)&half_word,&tempData,1,100);
//    return tempData;
//}

/*!
    \brief      poll the status of the write in progress(wip) flag in the flash's status register
    \param[in]  none
    \param[out] none


    \retval     none
*/
void spi_flash_wait_for_write_end(void)
{
    uint8_t flash_status = 0;

    /* select the flash: chip select low */
    FLASH_CSN_ASSERT();

    /* send "read status register" instruction */
    spi_flash_send_byte(RDSR);

    /* loop as long as the memory is busy with a write cycle */
    do{
        /* send a dummy byte to generate the clock needed by the flash
        and put the value of the status register in flash_status variable */
        flash_status = spi_flash_send_byte(DUMMY_BYTE);
    }while(SET == (flash_status & WIP_FLAG));

    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();

}


/* Exported functions --------------------------------------------------------*/

/*!
    \brief      get flash identification
    \param[in]  none
    \param[out] none
    \retval     flash identification
*/
uint32_t u32STORAGE_GetID(void)
{
    uint32_t temp = 0, temp0 = 0, temp1 = 0, temp2 = 0;

    /* select the flash: chip select low */
    FLASH_CSN_ASSERT();

    /* send "RDID " instruction */
    spi_flash_send_byte(RDID);

    /* read a byte from the flash */
    temp0 = spi_flash_send_byte(DUMMY_BYTE);

    /* read a byte from the flash */
    temp1 = spi_flash_send_byte(DUMMY_BYTE);

    /* read a byte from the flash */
    temp2 = spi_flash_send_byte(DUMMY_BYTE);

    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();

    temp = (temp0 << 16) | (temp1 << 8) | temp2;

    return temp;
}

/*!
    \brief      erase the specified flash sector
    \param[in]  sector_addr: address of the sector to erase
    \param[out] none
    \retval     none
*/
void vSTORAGE_EraseSec(uint32_t sector_addr)
{
    /* send write enable instruction */
    spi_flash_write_enable();

    /* sector erase */
    /* select the flash: chip select low */
    FLASH_CSN_ASSERT();

    /* send sector erase instruction */
    spi_flash_send_byte(SE);
    /* send sector_addr high nibble address byte */
    spi_flash_send_byte((sector_addr & 0xFF0000) >> 16);
    /* send sector_addr medium nibble address byte */
    spi_flash_send_byte((sector_addr & 0xFF00) >> 8);
    /* send sector_addr low nibble address byte */
    spi_flash_send_byte(sector_addr & 0xFF);
    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();


    /* wait the end of flash writing */
    spi_flash_wait_for_write_end();
}

/*!
    \brief      erase the entire flash
    \param[in]  none
    \param[out] none
    \retval     none
*/
void vSTORAGE_EraseAll(void)
{
    /* send write enable instruction */
    spi_flash_write_enable();

    /* bulk erase */
    /* select the flash: chip select low */
    FLASH_CSN_ASSERT();
    /* send bulk erase instruction  */
    spi_flash_send_byte(BE);
    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();

    /* wait the end of flash writing */
    spi_flash_wait_for_write_end();
}

/*!
    \brief      write more than one byte to the flash
    \param[in]  pbuffer: pointer to the buffer
    \param[in]  write_addr: flash's internal address to write
    \param[in]  num_byte_to_write: number of bytes to write to the flash
    \param[out] none
    \retval     none
*/
void vSTORAGE_PageWrite(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
    /* enable the write access to the flash */
    spi_flash_write_enable();

    /* select the flash: chip select low */
    FLASH_CSN_ASSERT();

    /* send "write to memory" instruction */
    spi_flash_send_byte(WRITE);
    /* send write_addr high nibble address byte to write to */
    spi_flash_send_byte((write_addr & 0xFF0000) >> 16);
    /* send write_addr medium nibble address byte to write to */
    spi_flash_send_byte((write_addr & 0xFF00) >> 8);
    /* send write_addr low nibble address byte to write to */
    spi_flash_send_byte(write_addr & 0xFF);

    /* while there is data to be written on the flash */
    while(num_byte_to_write--){
        /* send the current byte */
        spi_flash_send_byte(*pbuffer);
        /* point on the next byte to be written */
        pbuffer++;
    }

    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();

    /* wait the end of flash writing */
    spi_flash_wait_for_write_end();
}

/*!
    \brief      write block of data to the flash
    \param[in]  pbuffer: pointer to the buffer
    \param[in]  write_addr: flash's internal address to write
    \param[in]  num_byte_to_write: number of bytes to write to the flash
    \param[out] none
    \retval     none
*/
void vSTORAGE_BufferWrite(uint8_t* pbuffer, uint32_t write_addr, uint16_t num_byte_to_write)
{
    uint8_t num_of_page = 0, num_of_single = 0, addr = 0, count = 0, temp = 0;

    addr          = write_addr % SPI_FLASH_PAGE_SIZE;
    count         = SPI_FLASH_PAGE_SIZE - addr;
    num_of_page   = num_byte_to_write / SPI_FLASH_PAGE_SIZE;
    num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;

    /* write_addr is SPI_FLASH_PAGE_SIZE aligned */
    if(0 == addr){
        /* num_byte_to_write < SPI_FLASH_PAGE_SIZE */
        if(0 == num_of_page){
            vSTORAGE_PageWrite(pbuffer,write_addr,num_byte_to_write);
        }else{
            /* num_byte_to_write >= SPI_FLASH_PAGE_SIZE */
            while(num_of_page--){
                vSTORAGE_PageWrite(pbuffer,write_addr,SPI_FLASH_PAGE_SIZE);
                write_addr += SPI_FLASH_PAGE_SIZE;
                pbuffer += SPI_FLASH_PAGE_SIZE;
            }
            vSTORAGE_PageWrite(pbuffer,write_addr,num_of_single);
        }
    }else{
        /* write_addr is not SPI_FLASH_PAGE_SIZE aligned */
        if(0 == num_of_page){
            /* (num_byte_to_write + write_addr) > SPI_FLASH_PAGE_SIZE */
            if(num_of_single > count){
                temp = num_of_single - count;
                vSTORAGE_PageWrite(pbuffer,write_addr,count);
                write_addr += count;
                pbuffer += count;
                vSTORAGE_PageWrite(pbuffer,write_addr,temp);
            }else{
                vSTORAGE_PageWrite(pbuffer,write_addr,num_byte_to_write);
            }
        }else{
            /* num_byte_to_write >= SPI_FLASH_PAGE_SIZE */
            num_byte_to_write -= count;
            num_of_page = num_byte_to_write / SPI_FLASH_PAGE_SIZE;
            num_of_single = num_byte_to_write % SPI_FLASH_PAGE_SIZE;

            vSTORAGE_PageWrite(pbuffer,write_addr, count);
            write_addr += count;
            pbuffer += count;

            while(num_of_page--){
                vSTORAGE_PageWrite(pbuffer,write_addr,SPI_FLASH_PAGE_SIZE);
                write_addr += SPI_FLASH_PAGE_SIZE;
                pbuffer += SPI_FLASH_PAGE_SIZE;
            }

            if(0 != num_of_single){
                vSTORAGE_PageWrite(pbuffer,write_addr,num_of_single);
            }
        }
    }
}

/*!
    \brief      read a block of data from the flash
    \param[in]  pbuffer: pointer to the buffer that receives the data read from the flash
    \param[in]  read_addr: flash's internal address to read from
    \param[in]  num_byte_to_read: number of bytes to read from the flash
    \param[out] none
    \retval     none
*/
void vSTORAGE_BufferRead(uint8_t* pbuffer, uint32_t read_addr, uint16_t num_byte_to_read)
{
    /* select the flash: chip slect low */
    FLASH_CSN_ASSERT();

    /* send "read from memory " instruction */
    spi_flash_send_byte(READ);

    /* send read_addr high nibble address byte to read from */
    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
    /* send read_addr medium nibble address byte to read from */
    spi_flash_send_byte((read_addr& 0xFF00) >> 8);
    /* send read_addr low nibble address byte to read from */
    spi_flash_send_byte(read_addr & 0xFF);

    /* while there is data to be read */
    while(num_byte_to_read--){
        /* read a byte from the flash */
        *pbuffer = spi_flash_send_byte(DUMMY_BYTE);
        /* point to the next location where the byte read will be saved */
        pbuffer++;
    }

    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();
}

/*!
    \brief      check the specified Sector's state.
    \param[in]  read_addr
    \param[out] none
    \retval     itsBlank bTrue or bFalse
*/
bool bSTORAGE_ItsBlankSector(uint32_t read_addr)
{
    uint8_t data = 0;
    int ix = 0;
    /* select the flash: chip slect low */
    FLASH_CSN_ASSERT();

    /* send "read from memory " instruction */
    spi_flash_send_byte(READ);

    /* send read_addr high nibble address byte to read from */
    spi_flash_send_byte((read_addr & 0xFF0000) >> 16);
    /* send read_addr medium nibble address byte to read from */
    spi_flash_send_byte((read_addr& 0xFF00) >> 8);
    /* send read_addr low nibble address byte to read from */
    spi_flash_send_byte(read_addr & 0xFF);

    /* while there is data to be read */
    for(;ix<4096;++ix){
        /* read a byte from the flash */
        data = spi_flash_send_byte(DUMMY_BYTE);
        if(data!=0xff) break;

    }

    /* deselect the flash: chip select high */
    FLASH_CSN_DEASSERT();
    
    if(ix==4096) return true;
    else return false;
}